import java.util.HashMap;
import java.io.IOException;
import java.lang.Integer;
import java.lang.Double;
import java.lang.Math;
import java.lang.StringBuffer;


//how to run for dummy's (Danny Dalton)
//java MyAssembler test.s garbage
//look @ garbage.i.coe
//dedicated to danny dalton(always sleeping), god, nick swenson(always add), comp sci lab
//awesome superbowl sunday, the inventors of alochol
//umhhhhh..... yeah
//definately no thanks to the creator of this assignment
//just kidding (or not)
//i probably guess they will NEVER read these comments
//if you do i was definately kidding
//otherwise i was not
//hi it's 5 in the morning 
//go to line 327,356, 1610
public class MyAssembler extends Assembler
{


  public static int WORD   = 1;
  public static int FRONT = 1;
  public static int BACK= 0;
  public static int REG_SIZE = 4;
  public static int INST_LENGTH = 17;
  public static int LOADI       = 3;
  public static int IMM      = 1;
  public static int REGULAR = 0;

  public static int INSTRUCTION_LENGTH= 3;

  public static int ONE_LOAD = 1;
  public static int TWO_LOAD = 2;
  public static int THREE_LOAD = 3;
  public static double LONG_COM1 = Math.pow(2,12);
  public static double  LONG_COM2 = Math.pow(2,24);
  public static double LONG_COM3 = Math.pow(2,34);

  public static String IMM_REG = "4";
  private int num_loads = 0;
  //opcodes
  public static String AND      = "00";
  public static String OR       = "01";
  public static String XOR      = "02";
  public static String NOT      = "03";
  //NEW
  public static String NEG      = "04";
  public static String ADD      = "05";
  public static String SUB      = "06";
  //register
  public static String LOADI1   = "08";
  public static String LOADI2   = "09";
  public static String LOADI3   = "0a";
  public static String LOADIN   = "0b";
  public static String ADDI     = "0c";
  public static String SUBI     = "0d";
  //NEW
  public static String SLL      = "0e";
  public static String SRL      = "0f";

  
  //offset
  public static String BAL      = "10";
  public static String BEQ      = "11";
  public static String BNE      = "12";
  public static String BLT      = "13";
  public static String BLTEQ    = "14";
  public static String JMP      = "15";
  public static String JAL      = "16";
  public static String RET      = "17";


  public static String LOAD     = "18";
  public static String STORE    = "19";
  public static String IN       = "1a";
  public static String OUT      = "1b";
  public static String JALR     = "1e";
 

 
  HashMap <String,Integer> label_map;
  MyAssembler(String [] args) throws IOException
  {
    super(args);
  }
  // The student has to implement it for processing the labels
  void processLabel(String sourceCode)
  {
    System.out.println("label is "+sourceCode + "is at" + programCounter);
    label_map.put(sourceCode,new Integer(programCounter)); 
    //label_map.put(new Integer(programCounter+WORD),sourceCode); 
  }
  // The student has to implement it for generating the machine codes.
  String generateCode(Instruction instruction)
  {
    String temp= instruction.operator;
    Operand[] register = instruction.operands;
    String machine = "";
    int flag = REGULAR;

    //generates the op codes
    
    if (temp.equals("and")
        || temp.equals("halt"))     machine=AND; 
    else if (temp.equals("or"))     machine=OR ; 
    else if (temp.equals("xor"))    machine=XOR; 
    else if (temp.equals("not"))    machine=NOT;
    else if (temp.equals("NEG"))    machine=NEG;
    else if (temp.equals("add") 
            || temp.equals("mov")) machine=ADD;
    else if (temp.equals("sub"))    machine=SUB;

    else if (temp.equals("loadi1")) machine=LOADI1; 
    else if (temp.equals("loadi2")) machine=LOADI2; 
    else if (temp.equals("addi"))   machine=ADDI; 
    else if (temp.equals("subi"))   machine=SUBI; 
    else if (temp.equals("sll"))    machine=SLL; 
    else if (temp.equals("slr"))    machine=SRL; 

    else if (temp.equals("bal"))    machine=BAL; 
    else if (temp.equals("beq"))    machine=BEQ; 
    else if (temp.equals("bne"))    machine=BNE; 
    else if (temp.equals("blt"))    machine=BLT; 
    else if (temp.equals("blteq"))  machine=BLTEQ; 
    else if (temp.equals("jmp") )   machine=JMP; 
    else if (temp.equals("jal"))    machine=JAL; 
    else if (temp.equals("ret"))    machine=RET; 

    else if (temp.equals("load"))   machine=LOAD;
    else if (temp.equals("store") ) machine=STORE ; 
    else if (temp.equals("in"))     machine=IN; 
    else if (temp.equals("out"))    machine=OUT; 
    else if (temp.equals("jalr"))   machine=JALR; 
    else if (temp.equals("loadi3"))         
    {                             
      machine=  LOADI3;          
      flag = LOADI;
    }
    else
    {
       System.out.println("invalid instruct " + temp);
    }

    //this is for the HALT instr
    if (register.length == 0)
    {
        String expand = "";
        if(temp.equals("ret"))
        {
            expand += SUBI + "206,\n";    //mov stack ptj down 
            expand += LOAD + "a21,\n";     //move reg var 
            expand += LOAD + "b22,\n";     //move reg var 
            expand += LOAD + "c23,\n";     //move reg var
            expand += LOAD + "d24,\n";     //move reg var
            expand += LOAD + "420,\n";     //imm contains ret address
            expand+=  SUBI + "301,\n";     
            // load return address
            //load previous frame pointer

        }
        machine += bit_stuff(machine,INSTRUCTION_LENGTH,FRONT);
        machine = expand + machine;
        System.out.println("ret is " + machine);
    }

    //only one operand
    
    else if (register.length == 1)
    {
        System.out.println("machine is " + machine);
        //if we're jump/jal
       if(temp.equals("jalr") || temp.equals("jal")
          || temp.equals("jmp") )
       {
         String expand = "";
         if(temp.equals("jal")) 
         {
            expand =  STORE + "520,\n";    //save return address
            expand += STORE + "a21,\n";    //save locals:        
            expand += STORE + "b22,\n";                    
            expand += STORE + "c23,\n";                    
            expand += STORE + "d24,\n";                     
            expand += ADDI  + "206,\n";     //mov stack ptr up 
            expand += ADD   + "302,\n";     //moving frame ptr to sp
            /*
            expand += ADD   + "531,\n";     //incrementing the pc to nxt value
            expand += ADDI  + "331,\n";     //incrementing the pc to nxt value
            */
            // set new frame/stack pointer:
	    	//set new return address 
            //save frame pointer

        }  

           boolean temp_flag = false;
          System.out.println("       diff " + temp  + register[0].name);
          //grab the address and gen label 
          String new_loadi= gen_label_offset(new Long(register[0].name).longValue(),false);

          //stuff just in case to the back of the op cod
          String to_add = bit_stuff(IMM_REG,INSTRUCTION_LENGTH,FRONT);

          //add this is for the imm register
          machine += to_add;

          //add the label off set before the actual instr
          machine = expand + new_loadi + machine; 

          //update the pc based on how many loadis you take
          //programCounter +=machine.length()/INSTRUCTION_LENGTH - 1; 
          num_loads = 0;

         }
       
      if(register[0].getOperandType().equals("immediate" ))
      {
          //grab imm value 
          String reg1 = Long.toHexString(register[0].extractImmediate());
          boolean temp_flag = false;
          //reg1 = bit_stuff(reg1,4,BACK);

          //branch
          if(temp.equals("bal"))
          {
            int to_sub = 0;
            String to_add ="";

            //we're going to shove 3 hex to the instr op code 
            if(reg1.length() > 3)
            {
                to_add = reg1.substring(reg1.length()-3,reg1.length());
            }
            else
            {
                to_add = reg1.substring(0,reg1.length());
            }

            //stuff to fill
            to_add = bit_stuff(to_add,INSTRUCTION_LENGTH, BACK);

            //add to opcode
            machine +=to_add;
          }
          
          else
          {
                //otherwise treat as regular instr
                System.out.println("immediate bal ");

                reg1 = bit_stuff(reg1,INSTRUCTION_LENGTH,BACK);
                machine += reg1;
          }
      }
      else if(register[0].getOperandType().equals("register"))
      {
          //otherwise treat as regular instr
          String reg1 = Integer.toHexString(register[0].extractRegisterNumber());
          machine += reg1;
      } 

      //ensure we get 5 hex val
      machine = bit_stuff(machine,5 ,FRONT);
    }

    else if (register.length  == 2)
    {
      //go through each register
      for (int i = 0; i < register.length; i++)
      {
        
        if(register[i].getOperandType().equals("immediate" ))
        {
          String reg1 = Long.toHexString(register[i].extractImmediate());
          reg1 =bit_stuff(reg1,INSTRUCTION_LENGTH-1,BACK);
          machine += reg1;
        }

        else if(register[i].getOperandType().equals("register"))
        {
          String reg1 = Integer.toHexString(register[i].extractRegisterNumber());
          //reg1 =bit_stuff(reg1,4,BACK);
          machine += reg1;
        }

        // gen mov pseudo instr by including 0 inbetween
        if(i == 1 && temp.equals("mov")) 
        {
            machine += "0";
        }
      }
    }

    
    else if (register.length== 3)
    {
      String reg1 = Integer.toHexString(register[0].extractRegisterNumber());
      String reg2 = Integer.toHexString(register[1].extractRegisterNumber());
      String reg3 = "";
      String maybe = "";
      if(register[2].getOperandType().equals("immediate" ))
      {
          reg3 = Long.toHexString(register[2].extractImmediate());
      }
      else if(register[2].getOperandType().equals("register"))
      {
          reg3 = Integer.toHexString(register[2].extractRegisterNumber());
      }

      if(temp.equals("beq") || temp.equals("bne") ||
        (temp.equals("blt") || temp.equals("blteq")))
      {
        System.out.println("       diff " + temp  + register[2].name);
        //grab the off_set and gen labels
        maybe = gen_label_offset(new Long(register[2].name).longValue(),false);
        //add offset before op code
        machine =maybe +  machine;

        //we're going to replace offset with IMM REG
        reg3 = IMM_REG;
      }
      //this is for you nick you sexy thing
      machine += reg3 + reg1 + reg2;
      //machine += reg1 + reg2 + reg3;
    }
    else
    {
      return null;
    }
    if(machine.length()<  5)
    {
        //we did something wrong
        System.out.println("error");
        return "";
    }
    //machine =bit_stuff(machine,INST_LENGTH,FRONT);

    return machine;
  }

  // The student has to implement it for updating the ppcrogram counter.
  void updateProgramCounter(Instruction instruction)
  {
    String s = instruction.operator;
    if(s.equals("jmp") || s.equals("bne")|| s.equals("jal")|| s.equals("beq") || s.equals("blt") || s.equals("blteq"))
    {
        programCounter +=3;
    }
    if(s.equals("jal"))
    {
        programCounter+=7;
        //programCounter+=8;
    }
    if(s.equals("ret"))
    {
        programCounter+=7;
    }
    programCounter+=WORD ;
  }

  // The student has to implement it for initializing some of their own variables and data structures.
  void initialization()  throws IOException
  {
    label_map = new HashMap<String,Integer>();
  }

  // The student has to implement it
  // for replacing the labels used in instruction
  void replaceInstructionLabel(Instruction instruction)
  {
   
    String hack = IMM + "\n";
    String operator = instruction.operator;
    Operand[] register = instruction.operands;
    int label_position = 0;
    int pos = 0;
    boolean change = false ;

    //gen position and indicate we want to change 
    if (operator.equals("jmp")  || (operator.equals("jal"))||operator.equals("bal"))
    {
        pos = 0;
        change = !change;
    }
    else if(operator.equals("beq") || operator.equals("bne") ||
           (operator.equals("blt") || (operator.equals("blteq"))))
    {
        pos = 2;
        change = !change;
    }
    if(change)
    {
        if(operator.equals("bal") || operator.equals("beq") || operator.equals("bne") ||
           (operator.equals("blt") || (operator.equals("blteq"))))
        {  
            //grab label pos
            label_position = label_map.get((String)register[pos].name);
            //gen offset
            Integer temp_off_set = new Integer(label_position);
            System.out.println("i am " + operator + " at " +  programCounter+" looking for " + register[pos].name + " located at " + temp_off_set );
            int off_set = temp_off_set.intValue(); 
            off_set= off_set-programCounter + 1 ;

            //add offset to the name
            register[pos].name = new Integer(off_set).toString();

        }
        if(operator.equals("jalr") || operator.equals("jal") || operator.equals("jmp"))
        { 
            //grab label pos 
           label_position = label_map.get((String)register[pos].name);

           //take on mem label
           register[pos].name = "" + label_position;
        }
    }

  }
  String gen_label_offset(long off_set,boolean flag)
  {
    String new_string = "";
    String reg1 = "";
    reg1 = Integer.toHexString((int)off_set); 

    //neg off_set
    //we have to work backward
    if(off_set < 0)
    {
        if( off_set<LONG_COM1 )
        {
          String temp = reg1.substring(reg1.length()-3,reg1.length());
          new_string =  LOADIN + temp+ ",\n";
        }
        else if(off_set > LONG_COM1 && off_set <LONG_COM2   && !flag )
        {
          new_string =  LOADIN+ reg1.substring(reg1.length()-3,reg1.length()) + ",\n";
          String temp = reg1.substring(reg1.length()-6,reg1.length()-9);
          new_string += LOADI2 + temp + ",\n"; 

          System.err.println("FUCK YOU FOR JUMPING GREATER THAN 17 FUCKING RETARD");
        }
        else if(off_set> LONG_COM2  && off_set< LONG_COM3  && !flag)
        {
          new_string =  LOADIN+ reg1.substring(reg1.length()-3,reg1.length()) + ",\n";
          new_string +=  LOADIN+ reg1.substring(reg1.length()-6,reg1.length()-9) + ",\n";
          String temp = reg1.substring(reg1.length()-9,reg1.length()-12) ;

          new_string += LOADI3   + temp + ",\n";;
          System.err.println("FUCK YOU FOR JUMPNIG GREATER THAN 17 FUCKING RETARD");
        }
    }
    else if(off_set >=0)
    {
        //i'm to lazy to tell you what the rest of this shit does
        //we parse the shit out of reg1 up to 3 in this case i guess
        //then we take the LOADI opcode and take that shit onto the reg1
        //also we throw a , and \n just for shit and giggles
        //repeat for everything else
        if(reg1.length() < 3)
        {
            String temp = reg1.substring(0,reg1.length()) ;
            String to_add= bit_stuff(temp,3,BACK);
            new_string =  LOADI1 + to_add + ",\n";
            new_string += LOADI2 + "000" + ",\n";
            new_string += LOADI3 + "000" + ",\n";
        }
        else if(reg1.length() > 3 && reg1.length() < 6 && !flag )
        {
          new_string =  LOADI1 + reg1.substring(0,3) + ",\n";
          String temp = reg1.substring(3,reg1.length());
          String to_add= bit_stuff(temp,3,BACK);
          new_string += LOADI2 + to_add+ ",\n"; 
          new_string += LOADI3 + "000" + ",\n";

          System.err.println("FUCK YOU FOR JUMPING GREATER THAN 17 FUCKING RETARD");
        }
        else if(reg1.length() > 6 && reg1.length() < 9 && !flag)
        {
          new_string =  LOADI1 + reg1.substring(0,3) + ",\n";
          new_string += LOADI2 + reg1.substring(3,6) + ",\n";
          String temp = reg1.substring(6,reg1.length()) ;
          new_string += LOADI3   + ",\n";;
          System.err.println("FUCK YOU FOR JUMPNIG GREATER THAN 17 FUCKUING RETARD");
        }
    }
    else
    {
       //this demostrates how angry i get 
       System.err.print("You know you're just a fucking ass-hole. Why ask for apples");
       System.err.println("when you can get sweet sweet amazing oranges. just fuck you and go fuck yourself");
    }
    return new_string; 
   }

  // The student has to implement it for replacing the labels used in memory
  void replaceMemoryLabel()
  { 
    //yeah if  you want this down, well your shit out of luck

  }

  //we're going to stuff some bits here
  String bit_stuff(String to_stuff, int stuffing, int front)
  {
    
    String to_add = "";
    for(int i = 0; i<stuffing-to_stuff.length(); i++)
    {

      to_add+= "0";
    }
    if(front == FRONT)
    {
      to_stuff += to_add;
      
    }
    else
    {
      to_add += to_stuff;
      to_stuff = to_add;
    }
    return to_stuff;
  }

}
